All files / util misc.ts

91.89% Statements 34/37
72.22% Branches 13/18
100% Functions 7/7
91.18% Lines 31/34
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                2x                                   2x 2x     640x 640x 640x 12800x   640x 640x   2x   2x 137623x 124191x 107767x                 2x       240x         240x         2x 240x       240x 248x         240x                       2x       20x 20x   1x 19x 3x   16x                     2x   95x    
/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import { assert } from './assert';
 
export type EventHandler<E> = (value: E) => void;
 
/**
 * A union of all of the standard JS types, useful for cases where the type is
 * unknown. Unlike "any" this doesn't lose all type-safety, since the consuming
 * code must still cast to a particular type before using it.
 */
export type AnyJs = null | undefined | boolean | number | string | object;
 
// TODO(b/66916745): AnyDuringMigration was used to suppress type check failures
// that were found during the upgrade to TypeScript 2.4. They need to be audited
// and fixed.
// tslint:disable-next-line:no-any
export type AnyDuringMigration = any;
 
// tslint:disable-next-line:class-as-namespace
export class AutoId {
  static newId(): string {
    // Alphanumeric characters
    const chars =
      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let autoId = '';
    for (let i = 0; i < 20; i++) {
      autoId += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    assert(autoId.length === 20, 'Invalid auto ID: ' + autoId);
    return autoId;
  }
}
 
export function primitiveComparator<T>(left: T, right: T): number {
  if (left < right) return -1;
  if (left > right) return 1;
  return 0;
}
 
/** Duck-typed interface for objects that have an isEqual() method. */
export interface Equatable<T> {
  isEqual(other: T): boolean;
}
 
/** Helper to compare nullable (or undefined-able) objects using isEqual(). */
export function equals<T>(
  left: Equatable<T> | null | undefined,
  right: T | null | undefined
): boolean {
  Iif (left !== null && left !== undefined) {
    return !!(right && left.isEqual(right));
  } else {
    // HACK: Explicitly cast since TypeScript's type narrowing apparently isn't
    // smart enough.
    return (left as null | undefined) === right;
  }
}
 
/** Helper to compare arrays using isEqual(). */
export function arrayEquals<T>(left: Array<Equatable<T>>, right: T[]): boolean {
  Iif (left.length !== right.length) {
    return false;
  }
 
  for (let i = 0; i < left.length; i++) {
    Iif (!left[i].isEqual(right[i])) {
      return false;
    }
  }
 
  return true;
}
 
/**
 * Returns the largest lexicographically smaller string of equal or smaller
 * length. Returns an empty string if there is no such predecessor (if the input
 * is empty).
 *
 * Strings returned from this method can be invalid UTF-16 but this is sufficent
 * in use for indexeddb because that depends on lexicographical ordering but
 * shouldn't be used elsewhere.
 */
export function immediatePredecessor(s: string): string {
  // We can decrement the last character in the string and be done
  // unless that character is 0 (0x0000), in which case we have to erase the
  // last character.
  const lastIndex = s.length - 1;
  if (s.length === 0) {
    // Special case the empty string.
    return '';
  } else if (s.charAt(lastIndex) === '\0') {
    return s.substring(0, lastIndex);
  } else {
    return (
      s.substring(0, lastIndex) +
      String.fromCharCode(s.charCodeAt(lastIndex) - 1)
    );
  }
}
 
/**
 * Returns the immediate lexicographically-following string. This is useful to
 * construct an inclusive range for indexeddb iterators.
 */
export function immediateSuccessor(s: string): string {
  // Return the input string, with an additional NUL byte appended.
  return s + '\0';
}